package com.bianmaba.spring.security.database;

import com.bianmaba.spring.security.database.core.IResourceDetailsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.util.*;


@Service
@Transactional
public class DatabaseSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
    protected static final Logger LOG = LoggerFactory.getLogger(DatabaseSecurityMetadataSource.class);

    @Autowired
    private DatabaseSecurityProperties securityProperties;

    @Autowired
    private IResourceDetailsService resourceService;


    protected final Map<RequestMatcher, Collection<ConfigAttribute>> requestMapping = new HashMap<RequestMatcher, Collection<ConfigAttribute>>(0);

    protected List<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();

    protected Long lastUpdateCache = null;

    /**
     * 从数据库中查询所有有效资源
     *
     * @return
     */
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        LOG.debug("获取资源列表:getAllConfigAttributes()");
        List<ConfigAttribute> resources = resourceService.findAllResources();
        if (resources == null) {
            return configAttributes;
        }

        for (ConfigAttribute resource : resources) {
            String url = resource.getAttribute();
            if (url != null && !url.trim().isEmpty()) {
                configAttributes.add(new SecurityConfig(url));
                AntPathRequestMatcher matcher = new AntPathRequestMatcher(url);
                List<ConfigAttribute> roles = resourceService.findRolesOfResource(url);
                List<ConfigAttribute> list = new ArrayList<ConfigAttribute>(0);
                for (ConfigAttribute attribute : roles) {
                    if (!attribute.getAttribute().startsWith("ROLE_")) {
                        list.add(new SecurityConfig("ROLE_" + attribute.getAttribute()));
                    }
                }
                requestMapping.put(matcher, list);
            }
        }
        return configAttributes;
    }

    public boolean supports(Class<?> clazz) {
        return true;
    }

    // 返回所请求资源所需要的权限(对应系统中的角色)
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        if (lastUpdateCache == null || System.currentTimeMillis() - lastUpdateCache > securityProperties.getResourceCacheTime()) {
            configAttributes.clear();
            requestMapping.clear();
            getAllConfigAttributes();
            lastUpdateCache = System.currentTimeMillis();
        }
        Set<Map.Entry<RequestMatcher, Collection<ConfigAttribute>>> entries = this.requestMapping.entrySet();
        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : entries) {
            if (entry.getKey().matches(request)) {
                return entry.getValue();
            }
        }
        return null;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}
